package top.go2do.homeGoods.business.goods.service;

import jakarta.persistence.criteria.Predicate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import top.go2do.common.dao.ApplicationRepository;
import top.go2do.common.dao.TagRepository;
import top.go2do.common.dao.UserRepository;
import top.go2do.common.module.ApplicationPO;
import top.go2do.common.module.TagPO;
import top.go2do.common.module.TagSimplyVO;
import top.go2do.common.module.UserPO;
import top.go2do.common.module.pageQuery.PageResult;
import top.go2do.common.utils.IdUtils;
import top.go2do.homeGoods.business.goods.dao.GoodsRepository;
import top.go2do.homeGoods.business.goods.dao.GoodsTagRepository;
import top.go2do.homeGoods.business.goods.module.*;
import top.go2do.homeGoods.exception.GoodsErrorCode;
import top.go2do.homeGoods.exception.GoodsException;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description
 * @Author xingyj
 * @CreateTime 2025/6/18 13:07
 */
@Service
public class GoodsServiceImpl implements GoodsService {
    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private IdUtils idUtils;

    @Autowired
    private ApplicationRepository applicationRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private GoodsTagRepository goodsTagRepository;

    @Autowired
    private TagRepository tagRepository;

    @Override
    public GoodsPO create(GoodsCreateForm createForm) {
        String appId = createForm.getAppId();
        String goodsCode = createForm.getGoodsCode();
        String goodsName = createForm.getGoodsName();
        GoodsPO goodsPO = goodsRepository.findByAppIdAndGoodsCodeAndGoodsName(appId, goodsCode, goodsName);
        if (goodsPO != null) {
            throw new GoodsException(GoodsErrorCode.GOODS_NAME_EXIST);
        }
        goodsPO = new GoodsPO();
        BeanUtils.copyProperties(createForm, goodsPO);
        Long goodsId = idUtils.getSnowflakeId();
        Long userId = createForm.getUserId();

        goodsPO.setGoodsId(goodsId);
        goodsPO.setCreatedBy(userId);
        goodsPO.setUpdatedBy(userId);
        goodsRepository.save(goodsPO);
        return this.getById(goodsId);
    }

    @Override
    public void modifyById(GoodsModifyForm modifyForm) {
        Long goodsId = modifyForm.getGoodsId();
        GoodsPO goodsPO = this.getById(goodsId);
        String goodsName = modifyForm.getGoodsName();
        String goodsCode = goodsPO.getGoodsCode();
        String appId = goodsPO.getAppId();
        if (StringUtils.hasText(goodsName)) {
            GoodsPO target = goodsRepository.findByAppIdAndGoodsCodeAndGoodsName(appId, goodsCode, goodsName);
            if (target != null && !target.getGoodsId().equals(goodsId)) {
                throw new GoodsException(GoodsErrorCode.GOODS_NAME_EXIST);
            }
            goodsPO.setGoodsName(goodsName);
        }
        goodsPO.setUpdatedBy(modifyForm.getUserId());
        goodsRepository.save(goodsPO);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeById(Long id) {
        this.getById(id);
        goodsRepository.deleteById(id);
        goodsTagRepository.deleteByGoodsId(id);
    }

    @Override
    public GoodsPO getById(Long id) {
        GoodsPO goodsPO = goodsRepository.findById(id).orElse(null);
        if (goodsPO == null) {
            throw new GoodsException(GoodsErrorCode.GOODS_ID_NOT_EXIST);
        }
        return goodsPO;
    }

    @Override
    public PageResult<GoodsPO> pageQuery(GoodsQueryCondition condition) {
        Sort.Direction direction = Sort.Direction.fromString(condition.getDirection());
        Sort sort = Sort.by(direction, condition.getOrderBy());

        /**
         * pageNumber 从0开始
         */
        Integer page = condition.getPage() - 1;
        Integer size = condition.getSize();
        Pageable pageable = PageRequest.of(page, size, sort);
        Specification<GoodsPO> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (condition.getAppId() != null) {
                predicates.add(cb.equal(root.get("appId"), condition.getAppId()));
            }

            if (condition.getGoodsCode() != null) {
                predicates.add(cb.equal(root.get("goodsCode"), condition.getGoodsCode()));
            }

            if (condition.getCategoryId() != null) {
                predicates.add(cb.equal(root.get("categoryId"), condition.getCategoryId()));
            }

            if (condition.getGoodsName() != null) {
                predicates.add(cb.like(root.get("goodsName"), "%" + condition.getGoodsName() + "%"));
            }

            if (condition.getKeyword() != null) {
                Predicate appIdMatch = cb.like(root.get("goodsName"), "%" + condition.getKeyword() + "%");
                Predicate appNameMatch = cb.like(root.get("description"), "%" + condition.getKeyword() + "%");
                predicates.add(cb.or(appIdMatch, appNameMatch)); // 核心修改点
            }
            return cb.and(predicates.toArray(new Predicate[0]));
        };
        Page<GoodsPO> queryResult = goodsRepository.findAll(spec, pageable);
        PageResult<GoodsPO> pageResult = new PageResult(queryResult.getNumber(), queryResult.getSize(), queryResult.getTotalElements(), queryResult.getTotalPages());
        pageResult.setList(queryResult.getContent());
        return pageResult;
    }

    @Override
    public List<GoodsPO> listAll(GoodsQueryAllCondition condition) {
        Sort.Direction direction = Sort.Direction.fromString(condition.getDirection());
        Sort sort = Sort.by(direction, condition.getOrderBy());

        Specification<GoodsPO> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (condition.getAppId() != null) {
                predicates.add(cb.equal(root.get("appId"), condition.getAppId()));
            }

            if (condition.getGoodsCode() != null) {
                predicates.add(cb.equal(root.get("goodsCode"), condition.getGoodsCode()));
            }

            if (condition.getCategoryId() != null) {
                predicates.add(cb.equal(root.get("categoryId"), condition.getCategoryId()));
            }

            if (condition.getGoodsName() != null) {
                predicates.add(cb.like(root.get("goodsName"), "%" + condition.getGoodsName() + "%"));
            }

            if (condition.getKeyword() != null) {
                Predicate appIdMatch = cb.like(root.get("goodsName"), "%" + condition.getKeyword() + "%");
                Predicate appNameMatch = cb.like(root.get("description"), "%" + condition.getKeyword() + "%");
                predicates.add(cb.or(appIdMatch, appNameMatch)); // 核心修改点
            }
            return cb.and(predicates.toArray(new Predicate[0]));
        };
        return goodsRepository.findAll(spec, sort);
    }

    @Override
    public List<GoodsListVO> po2vo(List<GoodsPO> list) {
        Set<String> appIdSet = new HashSet<>();
        Set<Long> userIdSet = new HashSet<>();
        Set<Long> goodsIdSet = new HashSet<>();

        for (GoodsPO po : list) {
            appIdSet.add(po.getAppId());
            userIdSet.add(po.getCreatedBy());
            userIdSet.add(po.getUpdatedBy());
            goodsIdSet.add(po.getGoodsId());
        }
        List<ApplicationPO> appList = applicationRepository.findAllById(appIdSet);
        List<UserPO> userList = userRepository.findAllById(userIdSet);
        List<GoodsTagPO> goodsTagList = goodsTagRepository.findByGoodsIdIn(goodsIdSet);
        Set<Long> tagIdSet;
        if (goodsTagList != null) {
            tagIdSet = goodsTagList.stream().map(GoodsTagPO::getTagId).collect(Collectors.toSet());
        } else {
            tagIdSet = new HashSet<>();
        }
        List<TagPO> permList = tagRepository.findAllById(tagIdSet);

        Map<String, String> appId2NameMap;
        Map<Long, String> userId2NameMap;
        Map<Long, TagPO> tagId2POMap;
        Map<Long, Set<TagSimplyVO>> goodsId2TagListMap;
        if (appList != null) {
            appId2NameMap = appList.stream().collect(Collectors.toMap(ApplicationPO::getAppId, ApplicationPO::getAppName));
        } else {
            appId2NameMap = new HashMap<>(0);
        }

        if (userList != null) {
            userId2NameMap = userList.stream().collect(Collectors.toMap(UserPO::getUserId, UserPO::getUsername));
        } else {
            userId2NameMap = new HashMap<>(0);
        }
        if (permList != null) {
            tagId2POMap = permList.stream().collect(Collectors.toMap(TagPO::getTagId, po -> po));
        } else {
            tagId2POMap = new HashMap<>(0);
        }

        if (goodsTagList != null) {
            goodsId2TagListMap = new HashMap<>();
            for (GoodsTagPO goodsTagPO : goodsTagList) {
                Long goodsId = goodsTagPO.getGoodsId();
                Long permId = goodsTagPO.getTagId();
                Set<TagSimplyVO> permSet = goodsId2TagListMap.get(goodsId);
                if (permSet == null) {
                    permSet = new HashSet<>();
                }
                TagPO po = tagId2POMap.get(permId);
                TagSimplyVO tagVo = new TagSimplyVO();
                tagVo.setTagId(String.valueOf(po.getTagId()));
                tagVo.setTagName(po.getTagName());
                permSet.add(tagVo);
                goodsId2TagListMap.put(goodsId, permSet);
            }
        } else {
            goodsId2TagListMap = new HashMap<>(0);
        }

        List<GoodsListVO> voList = list.stream().map(po -> {
            GoodsListVO vo = new GoodsListVO();
            BeanUtils.copyProperties(po, vo);
            vo.setAppName(appId2NameMap.get(po.getAppId()));
            vo.setTagList(goodsId2TagListMap.get(po.getGoodsId()));
            vo.setCreatedByName(userId2NameMap.get(po.getCreatedBy()));
            vo.setUpdatedByName(userId2NameMap.get(po.getUpdatedBy()));
            vo.setGoodsId(String.valueOf(po.getGoodsId()));
            vo.setUpdatedBy(String.valueOf(po.getUpdatedBy()));
            vo.setCreatedBy(String.valueOf(po.getCreatedBy()));
            return vo;
        }).toList();
        return voList;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void setTags(GoodsTagsForm form) {
        Long goodsId = form.getGoodsId();
        GoodsPO po = this.getById(goodsId);
        po.setUpdatedBy(form.getUserId());
        goodsRepository.save(po);
        String appId = po.getAppId();
        /**
         * step: 先删除所有，再插入最新
         */
        goodsTagRepository.deleteByGoodsId(goodsId);
        List<GoodsTagPO> rpList = new ArrayList<>();
        form.getTagIdList().forEach(tagId -> {
            GoodsTagPO roleTagPO = new GoodsTagPO();
            roleTagPO.setGoodsId(goodsId);
            roleTagPO.setTagId(tagId);
            roleTagPO.setAppId(appId);
            rpList.add(roleTagPO);
        });
        goodsTagRepository.saveAll(rpList);
    }
}
